<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>jQuery源码分析4--回调对象$.Callbacks</title>
<link rel="stylesheet" type="text/css" href="../../../css/article_base.css" />
</head>
<body>
<pre>
<p class="title">概述</p>
本文主要介绍源码的回调对象$.Callbacks
PS：(349-817)的意思就是对应源码的349行到817行，源码版本是jquery-2.0.3.js。

<p class="title">$.callbacks()的应用</p>
<strong>1: add添加函数 fire全触发</strong>
function aaa() {
	alert(1);
}
function bbb() {
	alert(2);
}
var cb = $.Callbacks();		//C必须大写
cb.add(aaa);
cb.add(bbb);
cb.fire();			//依次弹出 1 2 观察者模式
cb.fire();                  	//再依次弹出 1 2  


<strong>2：add添加函数 fire全触发传参数</strong>
function aaa(n) {
	alert('aaa函数' + n);
}
function bbb(n) {
	alert('bbb函数' + n);
}
var cb = $.Callbacks();		 
cb.add(aaa);
cb.add(bbb);
cb.fire(111);			//依次弹出 aaa函数111 bbb函数111


<strong>3：定义$.Callbacks(flag)的flag参数</strong>
// once: 确保这个回调列表只执行一次(像一个递延 Deferred). 
// memory: 保持以前的值和将添加到这个列表的后面的最新的值立即执行调用任何回调 (像一个递延 Deferred). 
// unique: 确保一次只能添加一个回调(所以有没有在列表中的重复). 
// stopOnFalse: 当一个回调返回false时 中断调用 
function aaa() {
	alert(1);
}
function bbb() {
	alert(2);
}
//如果不加'once' 那就依次弹出1(第一个fire)  1 2(第二个fire)
//如果加了'once' 那就是只执行第一个fire 只弹出1
var cb = $.Callbacks('once');		 
cb.add(aaa);
cb.fire();					 
cb.add(bbb);
cb.fire();

function aaa() {
	alert(1);
}
function bbb() {
	alert(2);
}
//如果不加'memory' 那只弹出1 cb.add(bbb)在fire下面
//如果加了'memory' 那依次弹出1 2 因为cb.fire()先不触发 等所有add完毕再触发
var cb = $.Callbacks('memory');		 
cb.add(aaa);
cb.fire();					 
cb.add(bbb);

function aaa() {
	alert(1);
}
function bbb() {
	alert(2);
}
//如果不加'unique' 那依次弹出1 1
//如果加了'unique' 那去除重复的cb.add(aaa) 只弹出1 
var cb = $.Callbacks('unique');		 
cb.add(aaa);				 
cb.add(aaa);
cb.fire();

function aaa() {
	alert(1);
	return false;
}
function bbb() {
	alert(2);
}
//如果不加'stopOnFalse' 那依次弹出1 2s
//如果加了'stopOnFalse' 执行aaa return了false然后就终止触发了 所以只弹出1 
var cb = $.Callbacks('stopOnFalse');		 
cb.add(aaa);				 
cb.add(bbb);
cb.fire();	

function aaa() {
	alert(1);
	return false;
}
function bbb() {
	alert(2);
}
//组合模式 只触发一次fire 并且记忆 所以一次弹出1 2
var cb = $.Callbacks('once memory');	
cb.add(aaa);				 
cb.fire();	
cb.add(bbb);
cb.fire();


<strong>4：$.Callbacks()对象下的其他方法</strong>
function aaa() {
	alert(1);
	return false;
}
function bbb() {
	alert(2);
}
var cb = $.Callbacks();	
cb.add(aaa);				 
cb.add(bbb);
cb.has(aaa);		//判断函数列表是否有aaa函数	
cb.remove(aaa);		//把aaa函数从函数列表移除
cb.empty();		//把所有的函数从函数列表移除
cb.disable();		//禁用函数列表
cb.lock(aaa);		//锁定aaa函数
cb.locked();		//判断aaa函数是否锁定
cb.fired() 		//是否在之前至少一次fire()过		   
cb.fire();


<p class="title">(2880-3042) Callbacks的源码分析</p>
<strong>大致流程：</strong>
//var cb = $.Callbacks()后 jQuery.Callbacks执行完毕return self; self对象下有add fire remove等方法
//然后add(aaa); add(bbb); add(ccc) 将aaa bbb ccc函数存入到jQuery.Callbacks的list数组里面
//然后fire() 依次将数组list里面存入的函数一个个执行 完成全触发


<strong>具体分析：</strong>
jQuery.Callbacks = function( options ) {
	//options可能是空 可能是'once'单个flag情况 可能是'once memory'组合模式情况
	//如果是空那就jQuery.extend({}, options) 最终options = {};
	//然后先看2847-2856
	// var optionsCache = {};
	// function createOptions( options ) {
	// 	var object = optionsCache[ options ] = {};
	// 	jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) {
	// 		object[ flag ] = true;
	// 	});
	// 	return object;
	// }
	//如果是'once'单个flag 那通过createOptions( options ) 最终options = {'once': true}
	//如果是'once memory'组合模式 那通过createOptions( options ) 最终options = {'once': true, 'memory':true}
	options = typeof options === "string" ?
		( optionsCache[ options ] || createOptions( options ) ) :
		jQuery.extend( {}, options );

	var memory, fired, firing, firingStart, firingLength, firingIndex, list = [], stack = !options.once && [],
		//定义函数function fire(data){xxxx}; 触发的具体执行过程
		fire = function( data ) {
			memory = options.memory && data;
			fired = true;
			firingIndex = firingStart || 0;
			firingStart = 0;
			firingLength = list.length;
			firing = true;
			for ( ; list && firingIndex < firingLength; firingIndex++ ) {
				//循环 依次触发list数组存入的函数aaa bbb ccc
				if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
					memory = false;  
					break;
				}
			}
			firing = false;
			if ( list ) {
				if ( stack ) {
					if ( stack.length ) {
						//如果once
						fire( stack.shift() );
					}
				} else if ( memory ) {
					//如果'memory'
					list = [];
				} else {
					self.disable();
				}
			}
		},
		//定义self对象
		self = {
			//add方法
			add: function() {
				if ( list ) {
					var start = list.length;
					//执行add(aaa) add(bbb) add(ccc)
					(function add( args ) {
						jQuery.each( args, function( _, arg ) {
							var type = jQuery.type( arg );
							if ( type === "function" ) {
								if ( !options.unique || !self.has( arg ) ) {
									//将aaa bbb ccc函数依次push到List数组里
									list.push( arg );
								}
							} else if ( arg && arg.length && type !== "string" ) {
								add( arg );
							}
						});
					})( arguments );
					if ( firing ) {
						firingLength = list.length;
					} else if ( memory ) {
						firingStart = start;
						fire( memory );
					}
				}
				return this;
			},
			//remove方法
			remove: function() {
				if ( list ) {
					jQuery.each( arguments, function( _, arg ) {
						var index;
						while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
							list.splice( index, 1 );
							if ( firing ) {
								if ( index <= firingLength ) {
									firingLength--;
								}
								if ( index <= firingIndex ) {
									firingIndex--;
								}
							}
						}
					});
				}
				return this;
			},
			//has方法
			has: function( fn ) {
				return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
			},
			//empty方法
			empty: function() {
				list = [];
				firingLength = 0;
				return this;
			},
			//disable方法
			disable: function() {
				list = stack = memory = undefined;
				return this;
			},
			//disabled方法
			disabled: function() {
				return !list;
			},
			//lock方法
			lock: function() {
				stack = undefined;
				if ( !memory ) {
					self.disable();
				}
				return this;
			},
			//locked方法
			locked: function() {
				return !stack;
			},
			//fireWith方法
			fireWith: function( context, args ) {
				if ( list && ( !fired || stack ) ) {
					args = args || [];
					args = [ context, args.slice ? args.slice() : args ];
					if ( firing ) {
						stack.push( args );
					} else {
						//进入fireWith方法后 调用上面定义的fire函数 进行触发具体执行
						fire( args );
					}
				}
				return this;
			},
			//fire方法 
			fire: function() {
				//调用fire方法后 进入上面的fireWith方法
				self.fireWith( this, arguments );
				return this;
			},
			//fired方法
			fired: function() {
				return !!fired;
			}
		};

	//最终返回self对象 所以$.Callbacks()就是slef对象 最终使用就是$.Callbacks().add(xxx)等
	return self;
};


</pre>
</body>
</html>